// var IV = [0x7380166F,	0x4914B2B9,
//     0x172442D7,	0xDA8A0600,
//     0xA96F30BC,	0x163138AA,
//     0xE38DEE4D,	0xB0FB0E4E];
var IV = [1937774191, 1226093241, 388252375, -628488704, -1452330820, 372324522, -477237683, -1325724082];
var T = new Array(64);
function initT(){
    var i = 0;
    for(; i < 16; i++){
        T[i] = 0x79CC4519;
        //T[i] = 2055708042;
    }
    for(; i < 64; i++){
        T[i] = 0x7A879D8A;
        //T[i] = 2055708042
    }
}
function FF(x, y, z, j) {
    if(j >= 0 && j < 16){
        return (x ^ y ^ z);
    }
    else if(j >= 16 && j< 64){
        return ((x & y) | (x & z) | (y & z));
    }
    return 0;
}
function GG(x, y, z, j) {
    if(j >= 0 && j < 16){
        return (x ^ y ^ z);
    }
    else if(j >= 16 && j< 64){
        return ((x & y) | ((~x) & z));
    }
    return 0;
}
function rotateLeft(x, num){
    return ((x << num) & -1 | ((x & -1) >>> (32 - num)));
}
function P0(x){
    return (x ^ (rotateLeft(x, 9)) ^
        rotateLeft(x, 17));
}
function P1(x) {
    return (x ^ (rotateLeft(x, 15)) ^
        rotateLeft(x, 23));
}
function CF(inStr, nHash) {
    var w68 = new Array(68);
    var w64 = new Array(64);
    var i;
    for(i = 0; i < 16; i++){
        w68[i] = (inStr[i * 4 + 0] << 24) & 0xFF000000
            | (inStr[i * 4 + 1] << 16) & 0x00FF0000
            | (inStr[i * 4 + 2] << 8) & 0x0000FF00
            | (inStr[i * 4 + 3] << 0) & 0x000000FF;
    }
    for(i = 16; i < 68; i++){
        w68[i] = P1(w68[i - 16] ^ w68[i - 9] ^
            (rotateLeft(w68[i - 3], 15))) ^
            (rotateLeft(w68[i - 13], 7)) ^ w68[i-6];
    }
    for(i = 0; i < 64; i++){
        w64[i] = w68[i] ^ w68[i + 4];
    }
    var A,B,C,D,E,F,G,H,SS1,SS2,TT1,TT2;
    A = nHash[0];
    B = nHash[1];
    C = nHash[2];
    D = nHash[3];
    E = nHash[4];
    F = nHash[5];
    G = nHash[6];
    H = nHash[7];
    for(i = 0; i < 64; i++){
        //console.log(A,B,C,D,E,F,G,H);
        SS1 = rotateLeft(rotateLeft(A,12) + E +
            rotateLeft(T[i], i % 32),7);
        SS2 = SS1 ^ rotateLeft(A,12);
        TT1 = FF(A, B, C, i) + D + SS2 + w64[i];
        TT2 = GG(E, F, G, i) + H + SS1 + w68[i];
        D = C;
        C = rotateLeft(B,9);
        B = A;
        A = TT1;
        H = G;
        G = rotateLeft(F,19);
        F = E;
        E = P0(TT2);

    }
    nHash[0] ^= A;
    nHash[1] ^= B;
    nHash[2] ^= C;
    nHash[3] ^= D;
    nHash[4] ^= E;
    nHash[5] ^= F;
    nHash[6] ^= G;
    nHash[7] ^= H;
}
function runSM3(inMessage, messageLen, outDigest) {
    initT();
    /*
    * 消息填充，首先将比特”1“填入消息末尾，再添加K个”0“，k满足l+k+1≡418%512的最小非负整数
    * 最后再添加一个64位比特串，该比特串是长度l的二进制表示。
    * */
    //分组数
    const groupNum = Math.floor((messageLen + 1 + 8 + 64) / 64);
    //填充后的消息
    var msgBuf = new Array( groupNum * 64);
    for (i=0; i<msgBuf.length; i++){
        msgBuf[i] = 0;
    }
    for(i=0; i<inMessage.length; i++){
        msgBuf[i] = inMessage[i].charCodeAt();
    }
    // msgBuf = inMessage.slice(0, inMessage.length);
    //0x80转换为二进制为1000 0000;
    msgBuf[messageLen] = 0x80;
    //int为32位数据，64位中高32位为0；
    var i;
    for(i = 0; i < 4; i++){
        msgBuf[groupNum * 64 - i - 1] = (messageLen * 8) >> (i * 8) & 0xFF;
    }
    var hash = new Array(8);
    //赋初始值。
    for(i = 0; i < 8; i++){
        hash[i] = IV[i];
    }
    //迭代
    for(i = 0; i < groupNum; i++){
        var k = 0;
        var msg64 = [];
        for(var j = 64 * i; j < 64 * i + 64; j++){
            msg64[k++] = msgBuf[j];
        }
        CF(msg64, hash);
    }
    //保存最后结果，char一个字节，int四个字节。
    for(i = 0; i < 8; i++){
        outDigest[i * 4 + 0] = (hash[i] >> 24) & 0xFF;
        outDigest[i * 4 + 1] = (hash[i] >> 16) & 0xFF;
        outDigest[i * 4 + 2] = (hash[i] >> 8) & 0xFF;
        outDigest[i * 4 + 3] = (hash[i] >> 0) & 0xFF;
    }
}
function getSM3Hash(msg){
    const num = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
    const msgLen = msg.length;
    var outDigest = new Array(32);
    var outMsg = new Array();
    runSM3(msg, msgLen, outDigest);
    var strTest;
    for (var i = 0; i < num.length; i++)
        strTest += num[i];
    for (var i = 0; i < 32; i++){
        var x = Math.floor(outDigest[i] / 16) % 16;
        outMsg += num[x];
        x = outDigest[i] % 16;
        outMsg += num[x];
    }
    return outMsg;
}